﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IWMAG.Scripting.AST.ObjectModel;
using IWMAG.Scripting.AST.Declarations;
using IWMAG.Scripting.Types;
using IWMAG.Scripting.Interpreter;
using IWMAG.Scripting.Compiler;
using IWMAG.Scripting.Compiler.UserInfo;

namespace IWMAG.Scripting.AST.Statements {
    internal class ForeachStatement : CodeStatement {
        public VariableDeclaration Variable;
        public CodeExpression Source;
        public CodeStatement Body;

        public ForeachStatement(Compiler.Lexeme l, VariableDeclaration var, CodeExpression source, CodeStatement body)
            : base(l) {
            Variable = var;
            Source = source;
            Body = body;
        }

        public override void Verify(Helpers.CompileTable sym, List<Compiler.IUserInfo> error) {
            Variable.Verify(sym, error);
            sym.Variables[Variable.Name] = Variable.Type;

            Source.Verify(sym, error);
            Body.Verify(sym, error);

            if (!(Source.TypeOf() is ArrayType)) {
                SendError(error, "Second argument to foreach must be an array.");
            }

            if (!((ArrayType) Source.TypeOf()).BaseType.Equals(Variable.Type)) {
                SendError(error, "Foreach's source argument must be an array of the first's type.");
            }
        }

        // TODO: add continue and break stuff
        public override IEnumerable<Continuation> Execute(Interpreter.ScriptEngine engine, Helpers.RuntimeTable sym) {
            sym.OpenScope();
            sym.Variables[Variable.Name] = RunValue.NoValue;

            Reference<RunValue> source = RunValue.NoValue;
            foreach (Continuation wait in Source.ValueOf(engine, sym, source)) yield return wait;

            for (int i = 0; i < source.ReferencedValue.ArrayValue.Count; i++) {
                if (engine.KillExecution) {
                    yield return new Continuation(Continuation.Token.Killed);
                    yield break;
                }

                sym.Variables[Variable.Name] = source.ReferencedValue.ArrayValue[i];
                foreach (Continuation wait in Body.Execute(engine, sym)) yield return wait;
            }

            sym.CloseScope();
        }
    }
}
